package com.kent.hellokotlin.cClassAndObject

//类与继承

import java.util.*
import kotlin.collections.ArrayList

class Invoice { /**/ }

class Empty

//构造函数
/*在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分：它跟在类名（与可选的类型参数）后*/
class Person constructor(firstName: String)

/*如果主构造函数没有任何注解或者可见性修饰符，可以省略这个 constructor 关键字。*/
class Person1(firstName: String)

//主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块（initializer blocks）中。
//在实例初始化期间，初始化块按照它们出现在类体中的顺序执行，与属性初始化器交织在一起

class InitOrderDemo(name: String) {
    val firstProperty: String = "First Property: $name".also { println(it) }

    init {
        println("First init block that print $name")
    }

    val secondProperty = "Second Property: ${name.length}".also(::println)

    init {
        println("Second init block that print ${name.length}")
    }

    //请注意，主构造的参数可以在初始化块中使用。它们也可以在类体内声明的属性初始化器中使用：
    val upperName = name.toUpperCase(Locale.getDefault())

}

class Person2(val firstName: String, val lastName: String, var age: Int) { /*……*/ }

//如果构造函数有注解或可见性修饰符，这个 constructor 关键字是必需的，并且这些修饰符在它前面：
class Customer public constructor(name: String)

//次构造函数
//类也可以声明前缀有 constructor的次构造函数
class Person3(name: String) {
    private val children: MutableList<Person3> = ArrayList()

    constructor(name: String, parent: Person3) : this(name) {
        parent.children.add(this)
    }
}

/*初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句，
因此所有初始化块与属性初始化器中的代码都会在次构造函数体之前执行。
即使该类没有主构造函数，这种委托仍会隐式发生，并且仍会执行初始化块*/

class Constructors() {
    init {
        println("init block")
    }

    constructor(i: Int) : this() {
        println("construct block")
    }
}

fun fooConstruct() {
    Constructors()
    println("------------------------------------------")
    Constructors(1)
}


/*
如果一个非抽象类没有声明任何（主或次）构造函数，它会有一个生成的不带参数的主构造函数。
构造函数的可见性是 public。 如果你不希望你的类有一个公有构造函数，你需要声明一个带有非默认可见性的空的主构造函数
*/
class DoNetCreateMe private constructor() {
}


//创建类的实例
val invoice = Invoice()
val consumer = Customer("Up")

//继承
//在 Kotlin 中所有类都有一个共同的超类 Any，这对于没有超类型声明的类是默认超类：
class Example

//By default, Kotlin classes are final: they can’t be inherited.
//To make a class inheritable, mark it with the open keyword
open class Base(fName: String)

//如需声明一个显式的超类型，请在类头中把超类型放到冒号之后：
class Derived(firstName: String, secondName: String) : Base(firstName)

//如果派生类没有主构造函数，那么每个次构造函数必须使用 super 关键字初始化其基类型，或委托给另一个构造函数做到这一点。
//注意，在这种情况下，不同的次构造函数可以调用基类型的不同的构造函数：
class Derived2 : Base {
    constructor(firstName: String, secondName: String) : super(firstName)
}

//覆盖方法
//我们之前提到过，Kotlin 力求清晰显式。因此，Kotlin 对于可覆盖的成员（我们称之为开放）以及覆盖后的成员需要显式修饰符：
open class Shape {
    open fun draw() {}
    fun fill() {}
}

class Circle : Shape() {
    //Circle.draw() 函数上必须加上 override 修饰符。如果没写，编译器将会报错
    override fun draw() {
        super.draw()
        println()
    }

    //如果函数没有标注 open 如 Shape.fill()，那么子类中不允许定义相同签名的函数， 不论加不加 override
    //override fun fill(){}

    //将 open 修饰符添加到 final 类（即没有 open 的类）的成员上不起作用
    open fun fakeOpen() {}
}

open class Rectangle : Shape() {
    //标记为 override 的成员本身是开放的，也就是说，它可以在子类中覆盖。如果你想禁止再次覆盖，使用 final 关键字
    final override fun draw() {
    }
}

//覆盖属性
open class Shape1 {
    open val vertexCount: Int = 0
    open val aValField: Int = 0
    open var aVarField: Int = 0
}

class Rectangle1 : Shape1() {
    //在超类中声明然后在派生类中重新声明的属性必须以 override 开头，并且它们必须具有兼容的类型。
    //每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖
    override val vertexCount: Int = 4

    //你也可以用一个 var 属性覆盖一个 val 属性，但反之则不行
    override var aValField: Int = 5
    //override val aVarField: Int = 5
}

//请注意，你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
class Rectangle2(override val vertexCount: Int = 4) : Shape1()

//派生类初始化顺序
//在构造派生类的新实例的过程中，第一步完成其基类的初始化（在之前只有对基类构造函数参数的求值），因此发生在派生类的初始化逻辑运行之前。
open class Base1(val name: String) {
    init {
        println("Initializing Base")
    }

    open val size: Int =
        name.length.also { println("Initializing size in Base: $it") }
}

class Derived1(
    name: String,
    lastName: String
) : Base1(name.capitalize().also { println("Argument for base $it") }) {
    init {
        println("Initializing Derived")
    }

    override val size: Int =
        (super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}

//调用超类实现
open class Rectangle3 {
    open fun draw() {
        println("Drawing a rectangle with color $borderColor")
    }

    val borderColor: String = "black"
}

//派生类中的代码可以使用 super 关键字调用其超类的函数与属性访问器的实现：
class FillRectangle : Rectangle3() {
    override fun draw() {
        super.draw()
        println("Fill the rectangle with color $fillColor")
    }

    fun onDraw() {
        Filler().drawAndFill()
    }

    val fillColor = super.borderColor

    //在一个内部类中访问外部类的超类，可以通过由外部类名限定的 super 关键字来实现：super@Outer：
    inner class Filler {
        fun fill() {
            println("Filler fill the rectangle with color $fillColor")
        }

        fun drawAndFill() {
            super@FillRectangle.draw()
            fill()
            println("Filler drawn a filled rectangle with color ${super@FillRectangle.borderColor}")
        }
    }

}

//覆盖规则
open class Rectangle4 {
    open fun draw() {
        println("draw method from rectangle")
    }

    fun test() {

    }
}

interface Polygon {
    fun draw() {
        println("draw method from polygon")
    }
}

/*
可以同时继承 Rectangle 与 Polygon， 但是二者都有各自的 draw() 实现，所以我们必须在 Square 中覆盖 draw()， 并提供其自身的实现以消除歧义
*/
class Square : Rectangle4(), Polygon {
    override fun draw() {
        super<Rectangle4>.draw()
        super<Polygon>.draw()
    }
}

//抽象类
/*
类以及其中的某些成员可以声明为 abstract。 抽象成员在本类中可以不用实现。
需要注意的是，我们并不需要用 open 标注一个抽象类或者函数——因为这不言而喻
*/
abstract class Rectangle5 : Polygon {
    abstract override fun draw()
}


fun main() {
    Square().draw()
}